/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package demochess;

import Helper.ChessMessageHandler;
import Helper.SocketReceiver;
import Helper.SocketSender;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.logging.*;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

/**
 *
 * @author Nhattruong
 */
public class ChessController {

    private final JButton _btnStartHost;
    private final JButton _btnSearchHost;
    private final JButton _btnGiveUp;
    private final JButton[][] _chessTable;
    private boolean _allowServerWait;
    private ServerSocket _srvSocket;
    private final ChessController _thisValue;
    private SocketSender _ss;
    private SocketReceiver _sr;
    private int clickedPos = -1;
    private final JTextField _txtIpAddress;
    private final JTextField _txtPort;
    private boolean _isMyTurn = false;
    private final JLabel _lblClock;
    private final JLabel _lblRivalIP;
    private final JLabel _lblRivalPort;
    private final JLabel _lblTurn;
    private boolean _counting;
    private final CellRecommender _recommender;

    public ChessController(JButton btnStartHost, JButton btnSearchHost, JButton btnGiveUp,
            JButton[][] chessTable, JTextField txtIpAddress, JTextField txtPort, JLabel lblRivalIP,
            JLabel lblRivalPort, JLabel lblClock, JLabel lblTurn) {
        this._btnStartHost = btnStartHost;
        this._btnSearchHost = btnSearchHost;
        this._btnGiveUp = btnGiveUp;
        this._chessTable = chessTable;
        this._txtIpAddress = txtIpAddress;
        this._txtPort = txtPort;
        this._lblClock = lblClock;
        this._lblRivalIP = lblRivalIP;
        this._lblRivalPort = lblRivalPort;
        this._lblTurn = lblTurn;
        this._recommender = new CellRecommender(chessTable);
        this._btnGiveUp.setEnabled(false);


        _btnStartHost.setText("Start host");
        _btnSearchHost.setText("Search host");

        _btnStartHost.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                btnStartHost_Clicked(e);
            }
        });
        _btnSearchHost.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                btnSearchHost_Clicked(e);
            }
        });
        _btnGiveUp.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                btnGiveUp_Clicked(e);
            }
        });

        _thisValue = this;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                _chessTable[i][j].addActionListener(new CellClickListener(i, j) {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        clickCell(e, xPos, yPos);
                    }
                });
            }
        }
        _recommender.resetTable();
    }

    private void btnGiveUp_Clicked(ActionEvent e) {
        try {
            sendMessage("cmd:giveup");
            stopMatch();
        } catch (IOException ex) {
            Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void btnSearchHost_Clicked(ActionEvent e) {
        IpForm ipForm = new IpForm();
        int rs = JOptionPane.showConfirmDialog(null, ipForm, "Host Infomation", JOptionPane.OK_CANCEL_OPTION);
        if (rs == JOptionPane.OK_OPTION) {
            searchHost(ipForm.getIpAddress(), Integer.parseInt(ipForm.getPort()), _txtIpAddress.getText() + ":" + _txtPort.getText());
            _btnStartHost.setEnabled(false);
        }
    }

    private void btnStartHost_Clicked(ActionEvent e) {
        if (_btnStartHost.getText().equals("Start host")) {
            startHost();
            _btnStartHost.setText("Stop host");
            _btnSearchHost.setEnabled(false);
        } else {
            try {
                stopMatch();
                _btnStartHost.setText("Start host");
                _btnSearchHost.setEnabled(true);
            } catch (IOException ex) {
                Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    private void clickCell(ActionEvent e, int i, int j) {
        if (_isMyTurn) {
            if (_chessTable[i][j].getIcon() != null || clickedPos != -1) {
                if (clickedPos == -1) {
                    if (_recommender.makeRecommend(i, j)) {
                        clickedPos = i * 8 + j;
                    }
                } else {
                    if (i * 8 + j == clickedPos) {
                        _recommender.cleanHighlight();
                        clickedPos = -1;
                    } else {
                        if (_chessTable[i][j].getBackground() == _recommender.HIGHLIGHTCOLOR) {
                            if (_recommender.checkWhiteUpLevelMove(clickedPos, i * 8 + j)) {
                                UpLevelPanel pnlUpLevel = new UpLevelPanel();
                                JOptionPane.showMessageDialog(null,pnlUpLevel,"Up level", JOptionPane.QUESTION_MESSAGE);
                                sendMessage("uplv:" + clickedPos + "-" + (i * 8 + j) + "-" + pnlUpLevel.getSelectedChess());
                                upLevelPawn(clickedPos, i * 8 + j, pnlUpLevel.getSelectedChess());
                                clickedPos = -1;
                            } else {
                                sendMessage("move:" + clickedPos + "-" + (i * 8 + j));
                                move(clickedPos, i * 8 + j);
                                clickedPos = -1;
                            }
                        }
                    }
                }
            }
        }
    }

    public void startHost() {
        _allowServerWait = true;
        Thread t = new Thread(new Runnable() {

            public void run() {
                try {
                    _srvSocket = new ServerSocket(31312);
                    while (_allowServerWait) {
                        try {
                            Socket guestSocket = _srvSocket.accept();
                            if (guestSocket != null) {
                                _sr = new SocketReceiver(guestSocket, new ChessMessageHandler(_thisValue));
                                _allowServerWait = false;
                                _sr.start();
                                _ss = new SocketSender(guestSocket);
                                guestSocket = null;

                            }
                        } catch (Exception e) {
                        }
                    }
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
        t.start();
    }

    public void searchHost(String ip, int port, String currentAddress) {
        try {
            Socket hostSocket = new Socket(ip, port);
            if (hostSocket != null) {
                _ss = new SocketSender(hostSocket);
                _ss.SendMessage("cmd:askforjoin-" + currentAddress);
                _sr = new SocketReceiver(hostSocket, new ChessMessageHandler(this));
                _sr.start();
            } else {
                JOptionPane.showMessageDialog(null, "IP: " + ip + ", port: " + port + " was not found.");
            }

        } catch (UnknownHostException ex) {
            JOptionPane.showMessageDialog(null, "IP: " + ip + ", port: " + port + " was not found.");
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "IP: " + ip + ", port: " + port + " was not found.");
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendMessage(String message) {
        try {
            _ss.SendMessage(message);
        } catch (IOException ex) {
            Logger.getLogger(ChessTable.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void startMatch(String rivalIP, String rivalPort, boolean isMyTurn) {
        _lblRivalIP.setText(rivalIP);
        _lblRivalPort.setText(rivalPort);
        _lblTurn.setText(isMyTurn ? "Your turn" : "Rival's turn");
        _isMyTurn = isMyTurn;
        clickedPos = -1;
        _counting = true;
        Thread timer = new Thread(new Runnable() {

            public void run() {
                Date curent = new Date();
                while (_counting) {
                    try {
                        long distance = ((new Date()).getTime() - curent.getTime()) / 1000;
                        _lblClock.setText(distance / 3600 + ":" + (distance % 3600) / 60 + ":" + distance % 60);
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        });
        timer.start();
        _recommender.resetTable();
        _btnGiveUp.setEnabled(true);
    }

    public void stopMatch() throws IOException {
        _allowServerWait = false;
        if (_sr != null && _ss != null) {
            _sr.stopListen();
            _ss.stopSender();
        }
        if (_srvSocket != null) {
            _srvSocket.close();
            _srvSocket = null;
        }
        _counting = false;
        _btnSearchHost.setEnabled(true);
        _btnStartHost.setEnabled(true);
        _btnSearchHost.setText("Search host");
        _btnStartHost.setText("Start host");
        _lblClock.setText("0:00:00");
        _lblRivalIP.setText("N/A");
        _lblRivalPort.setText("N/A");
        _lblTurn.setText("N/A");
        _recommender.resetTable();
        _btnGiveUp.setEnabled(false);
    }

    public void move(int from, int to) {
        int xFrom = from / 8;
        int yFrom = from % 8;
        int xTo = to / 8;
        int yTo = to % 8;
        if (!_isMyTurn) {
            xFrom = 7 - from / 8;
            yFrom = 7 - from % 8;
            xTo = 7 - to / 8;
            yTo = 7 - to % 8;

        }
        if (_recommender.checkCastlingMove(xFrom * 8 + yFrom, xTo * 8 + yTo)) {
            if (yFrom == 0) {
                _chessTable[xFrom][yTo - 1].setIcon(_chessTable[xFrom][yFrom].getIcon());
                _chessTable[xFrom][yFrom].setIcon(null);
                _chessTable[xFrom][yTo - 2].setIcon(_chessTable[xFrom][yTo].getIcon());
                _chessTable[xFrom][yTo].setIcon(null);
            } else {
                if (yTo == 0) {
                    _chessTable[xFrom][yFrom - 1].setIcon(_chessTable[xFrom][yTo].getIcon());
                    _chessTable[xFrom][yTo].setIcon(null);
                    _chessTable[xFrom][yFrom - 2].setIcon(_chessTable[xFrom][yFrom].getIcon());
                    _chessTable[xFrom][yFrom].setIcon(null);
                } else {
                    if (yFrom == 7) {
                        _chessTable[xFrom][yTo + 1].setIcon(_chessTable[xFrom][yFrom].getIcon());
                        _chessTable[xFrom][yFrom].setIcon(null);
                        _chessTable[xFrom][yTo + 2].setIcon(_chessTable[xFrom][yTo].getIcon());
                        _chessTable[xFrom][yTo].setIcon(null);
                    } else {
                        _chessTable[xFrom][yFrom + 1].setIcon(_chessTable[xFrom][yTo].getIcon());
                        _chessTable[xFrom][yTo].setIcon(null);
                        _chessTable[xFrom][yFrom + 2].setIcon(_chessTable[xFrom][yFrom].getIcon());
                        _chessTable[xFrom][yFrom].setIcon(null);
                    }
                }
            }
        }
        _chessTable[xTo][yTo].setIcon(_chessTable[xFrom][yFrom].getIcon());
        _chessTable[xFrom][yFrom].setIcon(null);
        _recommender.cleanHighlight();
        _isMyTurn = !_isMyTurn;
        _lblTurn.setText(_isMyTurn ? "Your turn" : "Rival's turn");
        int scanResult = _recommender.scanWinner();
        if (scanResult == 0) {
            return;
        }
        if (scanResult == 1) {
            try {
                JOptionPane.showMessageDialog(null, "You lose!");
                stopMatch();
                return;
            } catch (IOException ex) {
                Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (scanResult == 2) {
            try {
                JOptionPane.showMessageDialog(null, "You win!");
                stopMatch();
                return;
            } catch (IOException ex) {
                Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void rivalGiveUp() {
        try {
            stopMatch();
            JOptionPane.showMessageDialog(null, "You win! Your rival was leave game or give up.");
        } catch (IOException ex) {
            Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void cancelRequest() {
        try {
            stopMatch();
            _btnStartHost.setEnabled(false);
            _btnSearchHost.setText("Search host");
            JOptionPane.showMessageDialog(null, "Your request was cancel by host!");
        } catch (IOException ex) {
            Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void receiveRequest(String rivalAddress) {
        int rs = JOptionPane.showConfirmDialog(null, "Do you want to accept request from " + rivalAddress, "Join request", JOptionPane.YES_NO_OPTION);

        if (rs == JOptionPane.YES_OPTION) {
            rs = JOptionPane.showConfirmDialog(null, "Do you want to take first turn?", "Join request", JOptionPane.YES_NO_OPTION);
            if (rs == JOptionPane.YES_OPTION) {
                String[] info = rivalAddress.split(":");
                sendMessage("cmd:startmatch-" + _txtIpAddress.getText() + ":" + _txtPort.getText() + ":secondturn");
                _isMyTurn = true;
                startMatch(info[0], info[1], true);
            } else {
                sendMessage("cmd:startmatch-" + _txtIpAddress.getText() + ":" + _txtPort.getText() + ":firstturn");
            }
        } else {
            try {
                sendMessage("cmd:cancelrequest");
                stopMatch();
                startHost();
            } catch (IOException ex) {
                Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    private void setCellBackground(int i, int j) {
        if (i % 2 == 0) {
            if (j % 2 == 0) {
                _chessTable[i][j].setBackground(new Color(255, 255, 255));
            } else {
                _chessTable[i][j].setBackground(new Color(114, 114, 114));
            }
        } else {
            if (j % 2 != 0) {
                _chessTable[i][j].setBackground(new Color(255, 255, 255));
            } else {
                _chessTable[i][j].setBackground(new Color(114, 114, 114));
            }
        }
    }

    public void upLevelPawn(int from, int to, int selectedChess) {
        if (_isMyTurn) {
            _recommender.upLevelPawn(from, to, selectedChess);
        } else {
            from = (7 - from / 8) * 8 + 7 - from % 8;
            to = (7 - to / 8) * 8 + 7 - to % 8;
            _recommender.upLevelPawn(from, to, selectedChess);
        }
        _recommender.cleanHighlight();
        _isMyTurn = !_isMyTurn;
        _lblTurn.setText(_isMyTurn ? "Your turn" : "Rival's turn");
        int scanResult = _recommender.scanWinner();
        if (scanResult == 0) {
            return;
        }
        if (scanResult == 1) {
            try {
                JOptionPane.showMessageDialog(null, "You lose!");
                stopMatch();
                return;
            } catch (IOException ex) {
                Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (scanResult == 2) {
            try {
                JOptionPane.showMessageDialog(null, "You win!");
                stopMatch();
                return;
            } catch (IOException ex) {
                Logger.getLogger(ChessController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
